package mlogger

import (
	"fmt"
	"gitee.com/dennis-mxx/gcode-common/menv"
	"gitee.com/dennis-mxx/gcode-common/mexception"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"io"
	"os"
	"runtime/debug"
	"strings"
	"time"
)

var InstanceZap *zap.SugaredLogger

var Adapter zapcore.CallerEncoder
var TRACE_ID = "traceId"

const (
	outputDir = "./logs/"
)

func InitZap(serverName string, logger *menv.Logger) {
	var level zapcore.Level
	switch logger.Level {
	case "debug":
	case "DEBUG":
		level = zapcore.DebugLevel
		break
	case "info":
	case "INFO":
		level = zapcore.InfoLevel
		break
	default:
		level = zapcore.InfoLevel
	}
	encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
		MessageKey:    "msg",
		LevelKey:      "level",
		TimeKey:       "time",
		CallerKey:     "file",
		NameKey:       "logger",
		StacktraceKey: "stacktrace",
		LineEnding:    zapcore.DefaultLineEnding,
		EncodeLevel:   zapcore.CapitalLevelEncoder,
		EncodeCaller:  ShortCallerEncoder,
		EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendString(t.Format("2006-01-02 15:04:05.000Z07:00"))
		},
		//EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendInt64(int64(d) / 1000000)
		},
	})

	logLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= level
	})

	errorLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl == zapcore.ErrorLevel
	})

	majorHook := getWriter(serverName+".log", logger)
	errorHook := getWriter(serverName+"-err.log", logger)
	// 最后创建具体的Logger
	core := zapcore.NewTee(
		zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), logLevel),
		zapcore.NewCore(encoder, zapcore.AddSync(majorHook), logLevel),
		zapcore.NewCore(encoder, zapcore.AddSync(errorHook), errorLevel),
	)

	// 需要传入 zap.AddCaller() 才会显示打日志点的文件名和行数, 有点小坑
	log := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel))
	InstanceZap = log.Sugar()
	defer log.Sync()
	InstanceZap.Info(fmt.Sprintf("Initialize [logger level %s] ", level))
}
func ShortCallerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
	// TODO: consider using a byte-oriented API to save an allocation.
	enc.AppendString(caller.TrimmedPath())
	if Adapter != nil {
		Adapter(caller, enc)
	}
}

func GetWriter(filename string, logger *menv.Logger) io.Writer {
	return getWriter(filename, logger)
}
func getWriter(filename string, logger *menv.Logger) io.Writer {
	dir := outputDir
	if logger != nil && logger.Dir != "" {
		dir = logger.Dir
	}

	if !strings.HasSuffix(dir, "/") {
		dir = dir + "/"
	}
	hook := zapcore.AddSync(&lumberjack.Logger{
		Filename:   dir + filename, //日志文件存放目录
		MaxSize:    15,             //文件大小限制,单位MB
		MaxBackups: 5,              //最大保留日志文件数量
		MaxAge:     15,             //日志文件保留天数
		Compress:   true,           //是否压缩处理
	})

	return hook
}

func FormatException(logger func() *TraceLogger) {
	defer func() {
		if err := recover(); err != nil {
			logger().Error(err)
		}
	}()
	if err := recover(); err != nil {
		FormatError(logger, err)
	}

}
func FormatError(logger func() *TraceLogger, error any) {
	if error == nil {
		return
	}
	stack := debug.Stack()
	var newStack []byte

	for newStackIndex, count, index := 0, 0, 0; index <= len(stack)/2; index++ {
		current := stack[index]
		if count <= 6 && current == 10 {
			count++
			continue
		}
		if count > 6 {
			if newStack == nil {
				newStack = make([]byte, len(stack)/2-index+1)
			}
			newStack[newStackIndex] = current
			newStackIndex++

			if current == 10 {
				newStack[newStackIndex] = 9
				newStackIndex++
				index++
			}
		}
	}

	message := strings.Trim(string(newStack), " ")
	var pr string
	if mexception.IsMException(error) {
		pr = fmt.Sprintf("\n\t%s\n\t%s0x1B", error, message)
	} else {
		pr = fmt.Sprintf("\n\tExceltion:\t【%s】\n\t%s0x1B", error, message)
	}
	logger().Error(pr)

}
